using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

using Crainiate.Data.Persistence.Cache;

namespace Crainiate.Data.Persistence.Providers.Labelling
{
    public class Ordinals: Dictionary<LabelHash, int>
    {
        #region Interface

        //Constructors       
        public Ordinals(Type type)
        {
            AddType(new TypeLabelHash(type));
        }

        public Ordinals(IQuery query)
        {
            AddQuery(new QueryLabelHash(query));
        }

        protected virtual int GetOrdinal(Type type)
        {
            int result = 1;

            //Loop through the existing collection and count the types that match
            foreach (LabelHash hash in this.Keys)
            {
                if (hash is ITypeLabel)
                {
                    ITypeLabel typeLabel= (ITypeLabel) hash;
                    if (typeLabel.Type.Equals(type)) result += 1;
                }
            }
            return result;
        }

        #endregion

        #region Implementation

        private void AddType(TypeLabelHash label)
        {
            //Add any underlying properties
            ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(label.Item);
            if (!cache.IsUnderlying)
            {
                TypeLabelHash parent = new TypeLabelHash(cache.BaseType, label);
                AddType(parent);
            }

            //Add the main type
            Add(label, GetOrdinal(label.Item));

            //Add any object properties
            if (cache.HasObjects)
            {
                foreach (PropertyInfo prop in cache.ReferenceProperties)
                {
                    PropertyLabelHash parent = new PropertyLabelHash(prop, label);
                    AddProperty(parent);
                }
            }
        }

        private void AddProperty(PropertyLabelHash label)
        {
            //Get any reference properties and add
            ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(label.Item.PropertyType);
            if (!cache.IsUnderlying)
            {
                TypeLabelHash parent = new TypeLabelHash(cache.BaseType, label);
                AddType(parent);
            }

            //Add the label
            Add(label, GetOrdinal(label.Item.PropertyType));

            //Add any object properties
            if (cache.HasObjects)
            {
                foreach (PropertyInfo prop in cache.ReferenceProperties)
                {
                    PropertyLabelHash parent = new PropertyLabelHash(prop, label);
                    AddProperty(parent);
                }
            }
        }

        private void AddQuery(QueryLabelHash label)
        {
            //Add the query type
            TypeLabelHash parent = new TypeLabelHash(label.Item.Type, label);
            AddType(parent);

            //Add any subqueries
            if (label.Item.SubQueries != null)
            {
                foreach (IQuery query in label.Item.SubQueries)
                {
                    QueryLabelHash parent2 = new QueryLabelHash(query, label);
                    AddQuery(parent2);
                }
            }
        }

        #endregion
    }
}
